/**
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package us.levk.tools.collections;

import java.util.AbstractQueue;
import java.util.Iterator;
import java.util.Queue;


/**
 * <p>This queue will construct new items when poll or peek is invoked while the wrapped queue is
 * empty. This implementation breaks strict Queue interface!</p>
 * 
 * <p>This is a product of a common pattern, factoring this code only makes sense. The constructed
 * item on an empty wrapped queue will be automatically offered to the wrapped queue and returned
 * on the peek(). In other words if <pre>defaultingQueue.isEmpty()</pre> is true then 
 * <pre>defaultingQueue.peek() == defaultingQueue.poll()</pre> is also true.
 * 
 * @author levk
 *
 */
public class DefaultingQueue<E> extends AbstractQueue<E> {

  /**
   * Factory to construct new items on empty queue.
   * 
   * @author levk
   *
   * @param <E>
   */
  public interface Factory<E> {

    public E make ();
  }

  /**
   * Wrapped queue
   */
  private final Queue<E> wrapped;
  /**
   * Factory
   */
  private final Factory<E> factory;

  public DefaultingQueue (Queue<E> wrapped, final Factory<E> factory) {
    this.wrapped = wrapped;
    this.factory = factory;
  }

  /* (non-Javadoc)
   * @see java.util.Queue#offer(java.lang.Object)
   */
  @Override public boolean offer (E e) { return wrapped.offer (e); }

  /* (non-Javadoc)
   * @see java.util.Queue#poll()
   */
  @Override public E poll () { return wrapped.isEmpty () ? factory.make () : wrapped.poll (); }

  /* (non-Javadoc)
   * @see java.util.Queue#peek()
   */
  @Override public E peek () {
    if (wrapped.isEmpty ()) {
      E result = factory.make ();
      wrapped.offer (result);
    }
    return wrapped.peek ();
  }

  /* (non-Javadoc)
   * @see java.util.AbstractCollection#iterator()
   */
  @Override public Iterator<E> iterator () { return wrapped.iterator (); }

  /* (non-Javadoc)
   * @see java.util.AbstractCollection#size()
   */
  @Override public int size () { return wrapped.size (); }
}
